package com.nf;

import org.apache.commons.dbutils.DbUtils;

import javax.sql.DataSource;
import java.sql.*;
import java.util.Arrays;


public class miniAbstractQueryRunner {
    private volatile boolean pmdKnownBroken = false;


    protected final DataSource ds;

    public miniAbstractQueryRunner() {
        ds = null;

    }

    /**
     * Constructor to control the use of <code>ParameterMetaData</code>.
     *
     * @param pmdKnownBroken
     *            Some drivers don't support
     *            {@link ParameterMetaData#getParameterType(int) }; if
     *            <code>pmdKnownBroken</code> is set to true, we won't even try
     *            it; if false, we'll try it, and if it breaks, we'll remember
     *            not to use it again.
     */
    public miniAbstractQueryRunner(boolean pmdKnownBroken) {
        this.pmdKnownBroken = pmdKnownBroken;
        ds = null;

    }


    public miniAbstractQueryRunner(DataSource ds) {
        this.ds = ds;
    }



    public miniAbstractQueryRunner(DataSource ds, boolean pmdKnownBroken) {
        this.pmdKnownBroken = pmdKnownBroken;
        this.ds = ds;

    }

    protected void close(Statement stmt) throws SQLException {
        miniDbUtils.close(stmt);
    }

    protected void close(Connection conn) throws SQLException {
        miniDbUtils.close(conn);
    }

    protected void close(ResultSet rs) throws SQLException{
        DbUtils.close(rs);
    }

    public DataSource getDataSource() {
        return this.ds;
    }

    protected Connection prepareConnection() throws SQLException {
        if (this.getDataSource() == null) {
            throw new SQLException(
                    "QueryRunner requires a DataSource to be "
                            + "invoked in this way, or a Connection should be passed in");
        }
        return this.getDataSource().getConnection();
    }

    protected void rethrow(SQLException cause, String sql, Object... params)
            throws SQLException {

        String causeMessage = cause.getMessage();
        if (causeMessage == null) {
            causeMessage = "";
        }
        StringBuffer msg = new StringBuffer(causeMessage);

        msg.append(" Query: ");
        msg.append(sql);
        msg.append(" Parameters: ");

        if (params == null) {
            msg.append("[]");
        } else {
            msg.append(Arrays.deepToString(params));
        }

        SQLException e = new SQLException(msg.toString(), cause.getSQLState(),
                cause.getErrorCode());
        e.setNextException(cause);

        throw e;
    }

    protected PreparedStatement prepareStatement(Connection conn, String sql)
            throws SQLException {
        PreparedStatement ps = conn.prepareStatement(sql);
        return ps;
    }

    public void fillStatement(PreparedStatement stmt, Object... params)
            throws SQLException {

        // check the parameter count, if we can
        ParameterMetaData pmd = null;
        if (!pmdKnownBroken) {
            try {
                //参数元数据简单的认为是描述sql中？
                pmd = stmt.getParameterMetaData();
                if (pmd == null) { // can be returned by implementations that don't support the method
                    pmdKnownBroken = true;
                } else {
                    //获取的就是sql语句中？的个数
                    int stmtCount = pmd.getParameterCount();
                    //值得数量的处理，没有值就为0
                    int paramsCount = params == null ? 0 : params.length;

                    //问号的数量与值得数量不匹配就报错
                    if (stmtCount != paramsCount) {
                        throw new SQLException("Wrong number of parameters: expected "
                                + stmtCount + ", was given " + paramsCount);
                    }
                }
            } catch (SQLFeatureNotSupportedException ex) {
                pmdKnownBroken = true;
            }

        }

        // nothing to do here
        if (params == null) {
            return;
        }


        for (int i = 0; i < params.length; i++) {
            if (params[i] != null) {
                stmt.setObject(i + 1, params[i]);
            } else {
                //设置列的空值，是调用setNull（列号，列的类型）这种语法
                // 当无法知道列的类型时，就指定为varchar类型，因为很多的jdbc 驱动是允许这样做得
                // 如果驱动实现了ParameterMetaData的getParameterType方法，那么就用此方法来精确地获取列的类型
                int sqlType = Types.VARCHAR;
                if (!pmdKnownBroken) {

                    try {
                        sqlType = pmd.getParameterType(i + 1);
                    } catch (SQLException e) {
                        pmdKnownBroken = true;
                    }
                }
                stmt.setNull(i + 1, sqlType);
            }
        }
    }
}
